home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / ValueRtn.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  35.3 KB  |  736 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:           Values.c    
  3.  
  4.     Contains:    Container Manager Common Value Routines
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <4>     8/22/96    EL        1376276: allow delete data always go to tmp
  15.                                     free list.
  16.          <3>     8/13/96    DM        1362809: disable containers on error
  17.          <2>     1/15/96    TJ        Cleaned Up
  18.          <5>     12/9/94    EL        #1182275 Optionally do not maintain
  19.                                                     continue flag.
  20.          <4>     9/30/94    EL        #1182275 Delete space in non-update merge
  21.                                                     go to free list.
  22.          <3>     9/22/94    EL        #1182275 Last fix put too much space on
  23.                                                     free list.
  24.          <2>     8/26/94    EL        #1182275 Put space potentially recoverable
  25.                                                     by merging into the temporary free list.
  26.          <4>      6/3/94    EL        Rename Values.h/c to ValueRtn.h/c.
  27.          <3>     5/10/94    EL        #1162327. ValueDoNotFree is no longer
  28.                                                     needed.
  29.          <2>      5/5/94    EL        Fix bug that some free space is not going
  30.                                                     to the free list.
  31.          <1>      2/3/94    EL        first checked in
  32.          <3>      1/6/94    EL        Check ValueDoNotFree flag before calling
  33.                                                     cmAddToFreeList.
  34.          <2>     11/4/93    EL        Do not put deleted space in target
  35.                                                     container into the free list.
  36.  
  37.     To Do:
  38. */
  39.  
  40. /*---------------------------------------------------------------------------*
  41.  |                                                                           |
  42.  |                          <<<   ValueRtn.c    >>>                          |
  43.  |                                                                           |
  44.  |                  Container Manager Common Value Routines                  |
  45.  |                                                                           |
  46.  |                               Ira L. Ruben                                |
  47.  |                                  7/22/92                                  |
  48.  |                                                                           |
  49.  |                     Copyright Apple Computer, Inc. 1992-1996              |
  50.  |                           All rights reserved.                            |
  51.  |                                                                           |
  52.  *---------------------------------------------------------------------------*
  53.  
  54.  This file contains various routines needed for value operations, e.g., CMWriteValueData()
  55.  CMDeleteValueData(), etc.  Some of these routines are used when applying updates at open
  56.  time (insert and delete data, move a value header).
  57.  */
  58.  
  59.  
  60. #include <stddef.h>
  61. #include <stdio.h>
  62.  
  63. #ifndef __CMTYPES__
  64. #include "CMTypes.h"
  65. #endif
  66. #ifndef __CM_API__
  67. #include "CMAPI.h"
  68. #endif
  69. #ifndef __LISTMGR__
  70. #include "ListMgr.h"
  71. #endif
  72. #ifndef __VALUEROUTINES__
  73. #include "ValueRtn.h"       
  74. #endif
  75. #ifndef __TOCENTRIES__
  76. #include "TOCEnts.h"   
  77. #endif
  78. #ifndef __TOCOBJECTS__
  79. #include "TOCObjs.h"   
  80. #endif
  81. #ifndef __CONTAINEROPS__
  82. #include "Containr.h"  
  83. #endif
  84. #ifndef __HANDLERS__
  85. #include "Handlers.h"
  86. #endif
  87. #ifndef __FREESPACE__
  88. #include "FreeSpce.h" 
  89. #endif
  90. #ifndef __SESSIONDATA__
  91. #include "Session.h"          
  92. #endif
  93. #ifndef __ERRORRPT__
  94. #include "ErrorRpt.h"      
  95. #endif
  96. #ifndef __UTILITYROUTINES__
  97. #include "Utility.h"        
  98. #endif
  99.  
  100.                                                                     CM_CFUNCTIONS
  101.  
  102. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  103. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  104. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  105. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  106. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  107.  
  108. #if CM_MPW
  109. #pragma segment CMValueOps
  110. #endif
  111.  
  112.  
  113. /*----------------------------------------------------------------------------------*
  114.  | cmGetStartingValue - find value and value offset corresponding to value position |
  115.  *----------------------------------------------------------------------------------*
  116.  
  117.  This returns the TOCValuePtr to the value entry which contains the stream starting 
  118.  offset, startingOffset.  Also returned in valueOffset is the offset within the returned
  119.  value entry which is the startingOffset'th byte.
  120.  
  121.  Note, NULL is returned for the value pointer if we cannot find the offset. This could 
  122.  happen when the startingOffset is beyond the end of the value.
  123.  
  124.  This routine is necessary because of continued values (segments) which are represented by
  125.  a list of TOCValue entries off of a TOCValueHdr.  Each entry represents a discontinous
  126.  segment of the value data which is always viewed as a stream of contiguous bytes even 
  127.  though they are not.  This routine is one of those that allows its caller to view the
  128.  stream as contiguous.
  129. */
  130.  
  131. TOCValuePtr cmGetStartingValue(TOCValueHdrPtr theValueHdr, CM_ULONG startingOffset, 
  132.                                                              CM_ULONG *valueOffset)
  133. {
  134.     TOCValuePtr        theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  135.     CM_ULONG    prevOffset, offset;
  136.     
  137.     if (theValue == NULL) return (NULL);                                    /* saftey test                                     */
  138.     
  139.     /* Global names are handled separately. There is only one value entry for these. The    */
  140.     /* reason we split these off is because there is no length in the value. The scanning    */
  141.     /* logic below assumes there is always a length in the value, which there is except     */
  142.     /* for this one special case.                                                                                                                    */
  143.     
  144.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {
  145.         offset = GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
  146.         if (offset <= startingOffset) return (NULL);
  147.         *valueOffset = startingOffset;
  148.         return (theValue);
  149.     }
  150.     
  151.     /* Many times the user is just appending on to the end of value. We can quickly test    */
  152.     /* for this and eliminate that possibility...                                                                                    */
  153.     
  154.     if (startingOffset >= theValueHdr->size)                            /* quick check for appending        */
  155.         return (NULL);
  156.         
  157.     /* So we don't have a global name and we're not appending.  But most values                     */
  158.     /* may not be continued either.  So if there is only one value there's not much             */
  159.     /* question of which value segment to use!                                                                                        */
  160.     
  161.     if (cmCountListCells(&theValueHdr->valueList) == 1) {
  162.         if (theValueHdr->size <= startingOffset) return (NULL);
  163.         *valueOffset = startingOffset;
  164.         return (theValue);
  165.     }
  166.     
  167.     /* Ok we got to scan the thing.  We potentially can limit the scan if we assume             */
  168.     /* that all the continued value segments are approximately the same size.  Then we         */
  169.     /* should scan the values low-to-high (left-to-right) by ascending offset if the             */
  170.     /* startingOffset is less than half the total size. We should scan the values hit-to-    */
  171.     /* low (right-to-left) if the startingOffset is more than half way.  That's as fancy     */
  172.     /* as we get.  A binary search would be nice.  But since we have a linked list the         */
  173.     /* scanning wouldn't really be minimized that much.                                                                        */
  174.     
  175.     if (startingOffset >= (theValueHdr->size/2)) {                /* scan right-to-left...                */
  176.         prevOffset = theValueHdr->size;                                            /* current max offset is size        */
  177.         theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList);
  178.         while (theValue) {                                                                    /* scan to we find it...                */
  179.             prevOffset -= theValue->value.notImm.valueLen;             /* offset to this segment            */
  180.             if (prevOffset <= startingOffset) break;                        /* break if we found it                */
  181.             theValue = (TOCValuePtr)cmGetPrevListCell(theValue);/* keep scanning...                        */
  182.         } /* while */
  183.     } else {                                                                                            /* scan left-to-right...                */
  184.         offset = 0;                                                                                    /* this is current max offset        */
  185.         while (theValue) {                                                                    /* scan to we find it...                */
  186.             prevOffset = offset;                                                                /* save current total offset    */
  187.             offset += theValue->value.notImm.valueLen;                    /* add size of this segment        */
  188.             if (offset > startingOffset) break;                                    /* break if we found it                */
  189.             theValue = (TOCValuePtr)cmGetNextListCell(theValue);/* keep scanning...                        */
  190.         } /* while */
  191.     }
  192.         
  193.     if (theValue == NULL) return (NULL);                                    /* offset out of range                    */
  194.     
  195.     *valueOffset = startingOffset - prevOffset;                        /* relativize the offset                */
  196.     return (theValue);                                                                        /* return value segment entry        */
  197. }
  198.  
  199.  
  200. /*------------------------------------------------------------------------------*
  201.  | cmRead1ValueData - copy data for a single value (segment) to caller's buffer |
  202.  *------------------------------------------------------------------------------*
  203.  
  204.  This routine copies the data for the specified value (NOT a value header -- continued
  205.  values are not worried about here), starting at the specified offset, TO the caller's
  206.  buffer.  A maximum of maxSize characters or the size of the data, which ever is smaller,
  207.  is copied.  The function returns the amount of data copied to the buffer.
  208.  
  209.  It is possible here that what we are reading is from a container TOC that was created for
  210.  updating.  Such a TOC are a mixture of entries from the base TOC and all of its updaters.
  211.  Each updater has its own handler package to access the value data in its respective
  212.  container.  To get at the proper handlers for the value we must know which container 
  213.  "owns" the value.  To that end each value segment entry (a TOCValue) has a container
  214.  pointer of its own.  This points to the container that created that value segment and
  215.  that's the container we use to get the proper handlers.  It is NOT necessarily to the 
  216.  container who "owns" the entire TOC.  It will be if we're not updating, but we never
  217.  count on that fact.
  218.  
  219.  Note, this routine handles the special cases for immediate data.
  220. */
  221.  
  222. CM_ULONG cmRead1ValueData(TOCValuePtr theValue, CM_UCHAR *buffer,
  223.                                                     CM_ULONG offset, CM_ULONG maxSize)
  224. {
  225.     ContainerPtr     container = theValue->container;    /* use container "owning" the value        */
  226.     CM_ULONG amountRead, len;
  227.     char                     *p;
  228.     
  229.     if (maxSize == 0) return (0);
  230.     
  231.     len = cmGet1ValueSize(theValue) - offset;                /* get size of the value we're reading*/
  232.     if (len == 0) return (0);                                                /* if offset too far, don't read            */
  233.     if (len > maxSize) len = maxSize;                                /* truncate if necessary to maxSize        */
  234.     
  235.     if (theValue->flags & kCMGlobalName) {                    /* read global names specially                */
  236.         p = GetGlobalName(theValue->value.globalName.globalNameSymbol);
  237.         memcpy(buffer, p + offset, (size_t)(amountRead = len));
  238.     } else if (theValue->flags & kCMImmediate) {        /* read immediates specially                    */
  239.         p = (CM_CHAR *)theValue->value.imm.ucharsValue;
  240.         memcpy(buffer, p + offset, (size_t)(amountRead = len));
  241.     } else {                                                                                /* non-immediate data                                    */
  242.         CMfseek(container, theValue->value.notImm.value + offset, kCMSeekSet);            
  243.         amountRead = CMfread(container, buffer, sizeof(CM_UCHAR), len);
  244.     }
  245.     
  246.     return (amountRead);                                                        /* return amount we read                            */
  247. }
  248.  
  249.  
  250. /*---------------------------------------------------------------------*
  251.  | cmOverwrite1ValueData - overwrite data for a single value (segment) |
  252.  *---------------------------------------------------------------------*
  253.  
  254.  This routine copies the data for the specified value (NOT a value header -- continued
  255.  values are not worried about here), starting at the specified offset, FROM the caller's
  256.  buffer. A maximum of size characters are overwritten to the value.  The function returns
  257.  the amount of data copied from the buffer to the value.
  258.  
  259.  Note, overwriting of global names is NOT allowed and ASSUMED not passed to this routine.
  260.  Immediates are, however, handled and can be overwritten up to their existing size.
  261.  
  262.  The reason we don't allow writing to global names is that they are kept in a binary tree
  263.  symbol table as a function of the names. If you could change them you would foul up the
  264.  binary tree search.  I don't feel like figuring out how to allow this case so screw it!
  265.  
  266.  One more thing.  The offset to the next free position in the container is NOT updated
  267.  here. Nor should it be.  We are overwriting existing, i.e., already written data.
  268. */
  269.  
  270. CM_ULONG cmOverwrite1ValueData(TOCValuePtr theValue, CM_UCHAR *buffer,
  271.                                                              CM_ULONG offset, CM_ULONG size)
  272. {
  273.     ContainerPtr     container = theValue->container->updatingContainer;
  274.     CM_ULONG             amountWritten, len;
  275.     CM_UCHAR             *p;
  276.     
  277.     if (size == 0) return (0);
  278.     
  279.     len = cmGet1ValueSize(theValue) - offset;                /* get size of the value we're writing*/
  280.     if (len == 0) return (0);                                                /* if offset too far, don't write            */
  281.     if (len > size) len = size;                                            /* truncate if necessary to size            */
  282.     
  283.     if (theValue->flags & kCMImmediate) {                        /* overwrite immediates specially            */
  284.         p = theValue->value.imm.ucharsValue;
  285.         memcpy(p + offset, buffer, (size_t)(amountWritten = len));
  286.     } else {                                                                                /* non-immediate data                                    */
  287.         CMfseek(container, theValue->value.notImm.value + offset, kCMSeekSet);            
  288.         amountWritten = CMfwrite(container, buffer, sizeof(CM_UCHAR), len);
  289.         if (amountWritten != len) {
  290.             Container_Disable(theValue->container);
  291.             Container_Disable(container);
  292.             ERROR1(CM_err_BadWrite, CONTAINERNAME);
  293.             return (0);
  294.         }
  295.     }
  296.     
  297.     return (amountWritten);                                                    /* return amount actually overwritten    */
  298. }
  299.  
  300.  
  301. /*-----------------------------------------------------------------*
  302.  | cmConvertImmediate - convert a immediate value to non-immediate |
  303.  *-----------------------------------------------------------------*
  304.  
  305.  This routine is called whenever an immediate data value must be converted to a
  306.  non-immediate.  It takes as parameters the pointer to the immediate value and returns
  307.  true if there are no errors.  On return the value will have been converted to a
  308.  non-immediate and the input segment CMValue changed to container the offset to the 
  309.  now written value.
  310. */
  311.  
  312. CMBoolean cmConvertImmediate(TOCValuePtr theValue)
  313. {
  314.     ContainerPtr   container;
  315.     TOCValueHdrPtr theValueHdr = theValue->theValueHdr;
  316.     CM_ULONG  valueSize, offset0, offset, actualSize;
  317.     
  318.     container = theValue->container->updatingContainer;    /* get container for the value...    */
  319.     valueSize = CMGetValueSize((CMValue)theValueHdr);        /* ...get current size of value...*/
  320.     
  321.     /* Reuse free space if we're allowed and there is some.  Otherwise just write to the    */
  322.     /* end of the container.                                                                                                                            */
  323.     
  324.     offset0 = cmGetFreeListEntry(container, valueSize, true, &actualSize);
  325.     if (actualSize != 0)
  326.         CMfseek(container, offset0, kCMSeekSet);                /* reuse free space                                    */
  327.     else {
  328.         offset0 = CMgetContainerSize(container);                /* get current size of container        */
  329.         CMfseek(container, 0, kCMSeekEnd);                            /* write data to end of container        */
  330.     }
  331.     
  332.     if (CMfwrite(container, theValue->value.imm.ucharsValue, sizeof(CM_UCHAR), valueSize) != valueSize) {
  333.         Container_Disable(theValue->container);
  334.         Container_Disable(container);
  335.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  336.         return (false);
  337.     }
  338.     
  339.     /* Redefine the value to be non-immediate...                                                                                    */
  340.     
  341.     (void)cmSetValueBytes(container, &theValue->value, Value_NotImm, offset0, valueSize);
  342.     theValue->flags = 0;
  343.     theValueHdr->valueFlags = ValueDefined;
  344.     
  345.     offset = offset0 + valueSize;                                            /* update logical OR physical EOF        */
  346.     if (actualSize == 0)
  347.         container->physicalEOF = offset;                                /* update next free container byte    */
  348.     SetLogicalEOF(offset);                                                        /* set logical EOF (may != physical)*/
  349.     
  350.     theValue->container = container;                                    /* owner is now updating container    */
  351.         
  352.     return (true);
  353. }
  354.  
  355.  
  356. /*--------------------------------------------------------------------------------------*
  357.  | cmInsertNewSegment - create and insert a new value segment to into an existing value |
  358.  *--------------------------------------------------------------------------------------*
  359.  
  360.  This routine is called by CMInsertValueData() to create a new value segment insert to be
  361.  inserted into a previously existing value.  It is also called when applying data insert
  362.  updates at open time (by the internal routine applyValueUpdateInstructions() in 
  363.   Update.c ).  The new insert data is already written to the (updating) container, and the
  364.  data is at container offset dataOffset.  It is size bytes long.  The insert is to be at
  365.  the specified segOffset within the value segment insBeforeValue. 
  366.  
  367.  The function returns a pointer to the newly created insert segment, or NULL if there is
  368.  an error and the error reporter returns.
  369.  
  370.  Data inserts fall into three cases:
  371.     
  372.      (1). Inserting into an immediate.  If the size of the immediate plus the size of the
  373.                 insert still fits in a immediate the result will remain an immediate.  Otherwise,
  374.                 the immediate is converted to a non-immediate and we have cases (2) or (3).  
  375.                 Immediates are handled differently for updating and like normal insertions
  376.                 processed separately.
  377.                 
  378.      (2). Inserting into the START of an existing non-immediate value segment (segOffset
  379.                offset 0).  The insert is made a segment and placed before the existing segment
  380.                 (insBeforeValue).
  381.                 
  382.      (3). Inserting into the MIDDLE of a non-immediate value segment.  The existing segment
  383.                 must be split into two segments and the insert segment placed between them.
  384.  
  385.  CMInsertValueData() handles case (1) and calls this routine for cases (2) and (3) after
  386.  it determines it has these cases and after it has written the new insert data to the
  387.  container.
  388.  
  389.  Updating treats immediates separately and thus, like CMInsertValueData(), only cases (2)
  390.  and (3) are possible here.  Indeed, it is because of updating that this routine exists
  391.  at all!   If we didn't have updating, this code could live directly in 
  392.  CMInsertValueData().  In CMInsertValueData() the caller explicitly passes the data which
  393.  CMInsertValueData() must write out to the container.  However, for updating, we already
  394.  have the data written in the updating container.  Both cases thus merge here to create
  395.  the segment for that data and insert it into the segment list in the appropriate place.
  396. */
  397.  
  398. TOCValuePtr cmInsertNewSegment(TOCValueHdrPtr theValueHdr, TOCValuePtr insBeforeValue,
  399.                                                              CM_ULONG segOffset, CM_ULONG dataOffset,
  400.                                                              CMSize size)
  401. {
  402.     ContainerPtr  container = theValueHdr->container->updatingContainer;
  403.     TOCValuePtr   theLeftValue, theInsertValue;
  404.     TOCValueBytes valueBytes;
  405.  
  406.     (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, dataOffset, size);
  407.     theInsertValue = cmCreateValueSegment(theValueHdr, &valueBytes, 0);
  408.     if (theInsertValue == NULL) return (NULL);                /* if insert failed, abort now            */
  409.  
  410.     theInsertValue->logicalOffset = insBeforeValue->logicalOffset + segOffset; 
  411.  
  412.     if (segOffset > 0) {                                                            /* split the segment to be inserted    */
  413.         theLeftValue = insBeforeValue;                                    /* use original seg for left portion*/
  414.         (void)cmSetValueBytes(container, &valueBytes, Value_NotImm,                      /*right value*/
  415.                                                     insBeforeValue->value.notImm.value+segOffset,         /*fix offset    */
  416.                                                     insBeforeValue->value.notImm.valueLen-segOffset);/* ...& len    */
  417.         insBeforeValue = cmCreateValueSegment(theValueHdr,/* create right portion of insert    */
  418.                                                                                     &valueBytes,/* with adjusted offset length        */
  419.                                                                                     theLeftValue->flags);
  420.         if (insBeforeValue == NULL) return (NULL);            /* something went wrong?                        */
  421.         insBeforeValue->logicalOffset = theInsertValue->logicalOffset; /* adjust logical offset    */
  422.         insBeforeValue->container = theLeftValue->container;/* container is still the same    */
  423. #if CMKEEP_CONTINUE_FLAG
  424.         theLeftValue->flags |= kCMContinued;                        /* flag the left portion as cont'd    */
  425.         theValueHdr->valueFlags |= ValueContinued;            /* echo flags in the header                    */
  426. #endif
  427.         theLeftValue->value.notImm.valueLen = segOffset;/* set size of left portion                    */
  428.         
  429.         /* Insert right portion after the left portion. The insBeforeValue pointer will now */
  430.         /* be pointing at the segment we want to insert the new segment before.                            */
  431.         
  432.         (void)cmInsertAfterListCell(&theValueHdr->valueList, insBeforeValue, theLeftValue);
  433.     } /* splitting segment */
  434.     
  435.     /* Now cases (2) and (3) come back together.  We insert the new segment in front of        */
  436.     /* the right segment pointed to by insBeforeValue.  That will make it a continued         */
  437.     /* value.                                                                                                                                                            */
  438.     
  439.     (void)cmInsertBeforeListCell(&theValueHdr->valueList, theInsertValue, insBeforeValue);
  440. #if CMKEEP_CONTINUE_FLAG
  441.     theInsertValue->flags = (CM_USHORT)(insBeforeValue->flags | kCMContinued); 
  442.     theValueHdr->valueFlags |= ValueContinued;                                    /* echo flags in the hdr    */
  443. #endif
  444.     theValueHdr->size += theInsertValue->value.notImm.valueLen; /* add to total value len    */
  445.  
  446.     return (theInsertValue);                                                    /* give back newly inserted segment    */
  447. }
  448.  
  449. /*--------------------------------------------------------------------*
  450.  | checkForReuse - check to see if data space can be reused in future |
  451.  *--------------------------------------------------------------------*
  452.  
  453.  This routine checks to see if the free space can be reused, reserved for
  454.  future, or cannot be reused at all.
  455. */
  456.  
  457. static void CM_NEAR checkForReuse(ContainerPtr container, 
  458.                                                                     TOCValuePtr theValueToFree, 
  459.                                                                     CM_ULONG offset, CM_ULONG size)
  460. {
  461.     /* If the value is in the updating container, we call cmAddToFreeList, which nomally    */
  462.     /* put it on the permanent free list, unless the container is open for read/write.        */
  463.     if (container == theValueToFree->container) {
  464.         if (container->alwaysToTmpFree)
  465.             cmAddToTmpFreeList(container, offset, size);
  466.         else
  467.             cmAddToFreeList(container, NULL, offset, size);
  468.     }
  469.     /* If we are merging, then any value on the top level container may be merged later.    */
  470.     /* So we can put it in the temporary free list.                                                                                */
  471.     else if ((theValueToFree->container->depth == 1) &&
  472.                      (container->useFlags & kCMMerging) &&
  473.                      (container->trackFreeSpace))
  474.             cmAddToTmpFreeList(container, offset, size);
  475. }
  476.  
  477. /*------------------------------------------------------------------------*
  478.  | CMDeleteToTmpFree - delete data from a value and put on temp free list |
  479.  *------------------------------------------------------------------------*
  480.  
  481.  Deletes size bytes from the value data starting at the offset.  Always put it on
  482.  the temp free list.
  483. */
  484.  
  485. void cmDeleteToTmpFree(TOCValueHdrPtr theValueHdr, CMCount offset, CMSize size)
  486. {
  487.     ContainerPtr      container     = theValueHdr->container->updatingContainer;
  488.     container->alwaysToTmpFree            = true;
  489.     CMDeleteValueData((CMValue)theValueHdr, offset, size);
  490.     container->alwaysToTmpFree            = false;
  491.     
  492. }
  493.  
  494. /*-----------------------------------------------------------------*
  495.  | cmDeleteSegmentData - delete data represented by value segments |
  496.  *-----------------------------------------------------------------*
  497.  
  498.  This routine is called by CMDeleteValueData() to delete value data  It is also called
  499.  when applying data deletion updates at open time (by the internal routine
  500.  applyValueUpdateInstructions() in  Update.c ).  All the data starting at offset 
  501.  startOffset, up to and including the end offset, endOffset, are to be deleted.  If the
  502.  start and end offsets span entire value segments, those segments are removed.
  503.  
  504.  Note, this routine is for non-immediate data deletions only.  CMDeleteValueData() handles
  505.  deletions on immediates.  For updating, immediates are handled differently and thus can't
  506.  get in here.
  507.  
  508.  The start and end may map into the middle of segments.  This means that deleions of 
  509.  partial segments is possible.  Both the start and end offsets may map into a single
  510.  segment.  In that case the segment must be split into twp segments.  The comments in the
  511.  code explain these cases.
  512. */
  513.  
  514. void cmDeleteSegmentData(TOCValueHdrPtr theValueHdr, CM_ULONG startOffset,
  515.                                                                                                           CM_ULONG endOffset)
  516. {
  517.     TOCValuePtr         theStartValue, theEndValue, thePrevValue, theNextValue, theValue,
  518.                                  theRtValue;
  519. #if CMKEEP_CONTINUE_FLAG
  520.     TOCValuePtr         thePrevValue1;
  521. #endif
  522.     ContainerPtr      container = theValueHdr->container->updatingContainer;
  523.     CM_ULONG              offset1, offset2, segMaxOffset, amountDeleted, totalDeleted;
  524.     TOCValueBytes  valueBytes;
  525.     
  526.     /* Map the start and end offsets into the offsets within their respective segments...    */
  527.     
  528.     theStartValue  = cmGetStartingValue(theValueHdr, startOffset, &startOffset);
  529.     theEndValue       = cmGetStartingValue(theValueHdr, endOffset,     &endOffset);
  530.     
  531.     /* We now have the start and end value segments and offsets within them.  Everything    */
  532.     /* between is to be deleted.  We will loop through the segments starting with                 */
  533.     /* theStartValue and up to theEndValue.  The startOffset and endOffset will be used     */
  534.     /* to delete the partial first and last segment portions.  Remember all this stuff         */
  535.     /* could be for a single segment or multiple segments.  All the "interior" segments        */
  536.     /* for a multi-segment delete are removed.  The partial segment cases take a little     */
  537.     /* more work.                                                                                                                                                    */
  538.     
  539.     thePrevValue = NULL;                                                            /* this is segment just processed        */
  540.     theValue          = theStartValue;                                         /* this is the segment to process        */
  541.     totalDeleted = 0;                                                                    /* sum total amount deleted                    */
  542.     
  543.     while (thePrevValue != theEndValue) {                            /* loop through the segments...            */
  544.         theNextValue = (TOCValuePtr)cmGetNextListCell(theValue);/* next now for seg delete    */
  545.         thePrevValue = theValue;                                                /* remember segment we're processing*/
  546.         
  547.         segMaxOffset = theValue->value.notImm.valueLen - 1;            /* get segments max offset    */
  548.         offset1 = (theValue == theStartValue) ? startOffset : 0;/* get segment offsets...        */
  549.         offset2 = (theValue == theEndValue    ) ? endOffset        : segMaxOffset;
  550.         
  551.         /* There are four cases depending on offset1 and offset2, i.e., the offsets within    */
  552.         /* the current segment we're dealing with each time around this loop:                                */
  553.         
  554.         /*    (1). The offsets specify the entire segment is to be deleted.    [///////]                    */
  555.         /*    (2). offset1 == 0 ==> delete left portion of segment.                    [////   ]                    */
  556.         /*     (3). offset2 == segMaxOffset == delete right portion.                    [   ////]                    */
  557.         /*    (4)  if not (1), (2), (3) delete interior portion.                        [  ///  ]                    */
  558.         
  559.         /* The last case requires us to split the segment into two segments which means we     */
  560.         /* must "manufacture" an additional segment.                                                                                */
  561.         
  562.         if (offset1 == 0 && offset2 == segMaxOffset) {    /* delete entire segment...                    */
  563.             if (cmCountListCells(&theValueHdr->valueList) == 1)    { /* if value going null...        */
  564.                 checkForReuse(container, theValue,                    /* add freed space to free list            */
  565.                                             theValue->value.notImm.value,        /* from the beginning of the value*/
  566.                                             theValue->value.notImm.valueLen); /* and take the whole thing.        */
  567.                 theValue->value.notImm.value         = 0;                /* ...define remaining (only) seg        */
  568.                 theValue->value.notImm.valueLen = 0;                /*    as a null value                                */
  569.                 theValue->flags = kCMImmediate;                            /*        a null immediate value                */
  570.                 theValueHdr->valueFlags |= ValueImmediate;    /*         echo flag in the value hdr        */
  571.             } else {                                                                            /* value not null (yet), delete seg    */
  572. #if CMKEEP_CONTINUE_FLAG
  573.                 if (cmGetNextListCell(theValue) == NULL) {    /* make sure of seg cont'd bit...        */
  574.                     thePrevValue1 = (TOCValuePtr)cmGetPrevListCell(theValue);
  575.                     if (thePrevValue1 != NULL)                                /* ...no longer cont'd if last seg    */
  576.                         thePrevValue1->flags &= ~kCMContinued;    /*    is to be deleted                            */
  577.                 }
  578. #endif
  579.                 checkForReuse(container, theValue,                    /* add freed space to free list            */
  580.                                             theValue->value.notImm.value,        /* from the beginning of the value*/
  581.                                             theValue->value.notImm.valueLen); /* and take the whole thing.        */
  582.                 cmDeleteListCell(&theValueHdr->valueList, theValue);
  583.                 CMfree(theValue);                                                        /* poof!                                                        */
  584.             }
  585.             amountDeleted = segMaxOffset + 1;                            /* this is how much we're deleting    */
  586.         } else if (offset1 == 0) {                                            /* delete left portion of 1st seg...*/
  587.             amountDeleted = offset2 + 1;                                    /* this is how much we're deleting    */
  588.             checkForReuse(container, theValue,                         /* add freed space to free list            */
  589.                                                 theValue->value.notImm.value,    /* ...this is freed left part                */
  590.                                                 amountDeleted);
  591.             theValue->value.notImm.value += amountDeleted;/* bump up offset                                        */
  592.             theValue->value.notImm.valueLen  = segMaxOffset - offset2; /* adjust length                */
  593.             theValue->logicalOffset += amountDeleted;            /* adjust starting logical offset        */
  594.         } else if (offset2 == segMaxOffset) {                        /* delete rt. portion of last seg...*/
  595.             amountDeleted = segMaxOffset - offset1 + 1;        /* this is how much we're deleting    */
  596.             checkForReuse(container, theValue,                         /* add freed space to free list            */
  597.                                         theValue->value.notImm.value + offset1,    /* this is freed right part */
  598.                                         amountDeleted);
  599.             theValue->value.notImm.valueLen = offset1;
  600.         } else {                                                                                /* delete interior portion of seg...*/
  601.             (void)cmSetValueBytes(container, &valueBytes, Value_NotImm,             /* rt. chunk        */
  602.                                                         theValue->value.notImm.value + offset2 + 1,    /* adjust offset*/
  603.                                                         segMaxOffset - offset2);                                        /* ...and length*/
  604.             theRtValue = cmCreateValueSegment(theValueHdr,/* create right portion                         */
  605.                                                                               &valueBytes, theValue->flags);
  606.             if (theRtValue == NULL) return;                                /* something went wrong?                        */
  607.             theRtValue->logicalOffset = theValue->logicalOffset+offset2+1; /* fix log. offset    */
  608.             theRtValue->container = theValue->container;    /* rt. seg is same container as left*/ 
  609.             (void)cmInsertAfterListCell(&theValueHdr->valueList, theRtValue, theValue);
  610. #if CMKEEP_CONTINUE_FLAG
  611.             theValue->flags |= kCMContinued;                            /* flag the left portion as cont'd    */
  612. #endif
  613.             theValue->value.notImm.valueLen = offset1;        /* set size of left portion                    */
  614.             amountDeleted = offset2 - offset1 + 1;                /* this is how much we're deleting    */
  615.             checkForReuse(container, theValue,                         /* add freed space to free list            */
  616.                                         theValue->value.notImm.value + offset1,        /* this is "hole" created */
  617.                                                  amountDeleted);
  618.         }
  619.     
  620.         totalDeleted += amountDeleted;                                    /* tally total amount we delete            */
  621.         theValue = theNextValue;                                                 /* point at next segment                        */
  622.     }    /* while */                                                                            /* loop...                                                    */
  623.  
  624.     theValueHdr->size -= totalDeleted;                                /* update total size in value hdr        */
  625. #if CMKEEP_CONTINUE_FLAG
  626.     if (cmCountListCells(&theValueHdr->valueList) < 2)/* make sure of cont'd bit in hdr        */
  627.         theValueHdr->valueFlags &= ~ValueContinued;
  628. #endif
  629. }
  630.  
  631.  
  632.  /*---------------------------------------------------------------------*
  633.   | cmMoveValueHdr - move a value (header) to a new position in the TOC |
  634.   *---------------------------------------------------------------------*
  635.  
  636.  This routine is called by CMMoveValueData() to move a value (header).  It is also called
  637.  when applying "inserted" (move) updates at open time (by the internal routine
  638.  applyValueUpdateInstructions() in  Update.c ).   The value is physically deleted from its
  639.  original object/property as if a CMDeleteValue() were done on it.  If the value deleted
  640.  is the only one for the property, the property itself is deleted as in
  641.  CMDeleteObjectProperty(). 
  642.  
  643.  The value is added to the "to"s object propery in a manner similar to a CMNewValue(). The
  644.  order of the values for both the value's original object property and for the value's
  645.  new object property may be changed.
  646.  
  647.  Note, that although the effect of a move is a combination CMDeleteValue()/CMNewValue(),
  648.  THE INPUT REFNUM REMAINS VALID!  Its association is now with the new object property.
  649.  
  650.  The input refNum is returned as the function result.  NULL is returned if there is an 
  651.  error and the error reporter returns.
  652. */
  653.  
  654. TOCValueHdrPtr cmMoveValueHdr(TOCValueHdrPtr theFromValueHdr, CMObject object, CMProperty property)
  655. {
  656.     TOCObjectPtr      theFromObject, theToObject;
  657.     TOCPropertyPtr theFromProperty;
  658.     TOCValueHdrPtr theToValueHdr, prev, next;
  659.     ContainerPtr     container = theFromValueHdr->container;
  660.  
  661.     /* We will need pointers to the the "from" object and its property...                                    */
  662.     
  663.     theFromProperty = theFromValueHdr->theProperty;
  664.     theFromObject     = theFromProperty->theObject;
  665.     
  666.     /* For the hell of it see if the "from" and "to" represent the SAME place.  There's        */
  667.     /* no sense going on with this turkey if they are!                                                                        */
  668.     
  669.     if (theFromObject == (TOCObjectPtr)object)
  670.         if (cmFindObject(container->toc, theFromProperty->propertyID) == (TOCObjectPtr)property)
  671.             return (theFromValueHdr);                                                    /* incredible, ain't it?                */
  672.         
  673.     /* Now that we got all of that stuff out of the way we can proceed. The easiest way to    */
  674.     /* do the move is to create a dummy value entry in the "to" object property chain and    */
  675.     /* then do list manipulations on it to move the "from" value header and its value            */
  676.     /* chain.  We then must make sure to free the dummy we just created.  Doing it this        */
  677.     /* way allows us to use the mechanisms already in place without providing special         */
  678.     /* case code or routines.  For example, we don't have to worry about creating the            */
  679.     /* property at the destination if it does not happen to previously exist.                            */
  680.         
  681.     /* The first thing we do is remove the "from" value header from its property list.        */
  682.     /* This does not free the space.  It only jumps it out of the links.                                     */
  683.     
  684.     cmDeleteListCell(&theFromValueHdr->theProperty->valueHdrList, theFromValueHdr);
  685.     prev = (TOCValueHdrPtr)cmGetPrevListCell(theFromValueHdr); /* save for error recovery    */
  686.     next = (TOCValueHdrPtr)cmGetNextListCell(theFromValueHdr);
  687.     cmNullListLinks(theFromValueHdr);
  688.     
  689.     /* Now we can create the dummy "to" value header place holder.  For lack of anything    */
  690.     /* better, we give it the same flags, etc. from the "from" value.  By giving it the     */
  691.     /* same type, we automatically get the check for duplicate types in the "to" property.*/
  692.     
  693.     theToObject = cmDefineObject(container, ((TOCObjectPtr)object)->objectID,
  694.                                                              ((TOCObjectPtr)property)->objectID,
  695.                                                              theFromValueHdr->typeID,      /* error if dup                             */
  696.                                                              NULL, theFromValueHdr->generation, 0, 
  697.                                                              theFromObject->objectFlags,
  698.                                                              &theToValueHdr);
  699.                                                              
  700.     if (theToObject == NULL) {                                                            /* if something is wrong...        */
  701.         if (prev != NULL)                                                                            /* put back original "from"        */
  702.             cmInsertAfterListCell(&theFromValueHdr->theProperty->valueHdrList, theFromValueHdr, prev);
  703.         else if (next != NULL)
  704.             cmInsertBeforeListCell(&theFromValueHdr->theProperty->valueHdrList, theFromValueHdr, next);
  705.         else
  706.             cmAppendListCell(&theFromValueHdr->theProperty->valueHdrList, theFromValueHdr);
  707.         return (NULL);
  708.     }
  709.     
  710.     /* Now we want to replace the value header we just created with the "from" value            */
  711.     /* header (which is now deleted off the "from" property list and only pointed to by        */
  712.     /* theFromValueHdr).  That will carry along all the value segments on the value             */
  713.     /* header's chain and all the proper field settings in the value header as well.            */
  714.     
  715.     /* We play some list manipulation games to move the "from" value header to the "to"     */
  716.     /* list.  We know where the "to" dummy header was placed, theToValueHdr. So all we         */
  717.     /* have to do is insert the "from" value header before the dummy and then delete the     */
  718.     /* dummy.  We also now have to set the owning property field to point to the owning     */
  719.     /* property on the new list.                                                                                                                    */
  720.     
  721.     cmInsertBeforeListCell(&theToValueHdr->theProperty->valueHdrList, theFromValueHdr, theToValueHdr);
  722.     theFromValueHdr->theProperty = theToValueHdr->theProperty;
  723.     cmDeleteListCell(&theToValueHdr->theProperty->valueHdrList, theToValueHdr);
  724.     CMfree(theToValueHdr);                                                                        /* done with the dummy            */
  725.     
  726.     /* That was easy!  Now we look at the property for "from" value header and delete it    */
  727.     /* if there are no more values.                                                                                                             */
  728.     
  729.     if (cmIsEmptyList(&theFromProperty->valueHdrList))
  730.         CMfree(cmDeleteListCell(&theFromObject->propertyList, theFromProperty));
  731.         
  732.     return (theFromValueHdr);                                                                    /* returned move refNum            */
  733. }
  734.                                                           
  735.                                                             CM_END_CFUNCTIONS
  736.